package com.projectsexception.festivales.news;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.projectsexception.festivales.news.model.Feed;
import com.projectsexception.festivales.news.model.FeedEntry;
import com.projectsexception.festivales.news.model.FeedType;
import com.projectsexception.festivales.util.DateFormater;

public class FeedHandler extends DefaultHandler {
    
    private Feed feed;
    private FeedEntry entry;
    private int currentstate;
    private boolean entries;
    private FeedType feedType;
    private boolean feedTitle;
    private boolean feedLink;
    
    private static final int STATE_TITLE = 1;
    private static final int STATE_LINK = 2;
    private static final int STATE_PUBDATE = 3;


    public FeedHandler() {
        currentstate = 0;
        entries = false;
        feedTitle = false;
        feedLink = false;
    }

    /*
     * getFeed - this returns our feed when all of the parsing is complete
     */
    public Feed getFeed() {
        return feed;
    }

    public void startDocument() throws SAXException {
        // initialize our RSSFeed object - this will hold our parsed contents
        feed = new Feed();
    }

    public void endDocument() throws SAXException {
    }

    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
        String name = getElementName(localName, qName);
        if (feedType == null) {
            if (name.equalsIgnoreCase(FeedType.RSS.getRootElement())) {
                feedType = FeedType.RSS;
            } else if (name.equalsIgnoreCase(FeedType.RDF.getRootElement())) {
                feedType = FeedType.RDF;
            } else if (name.equalsIgnoreCase(FeedType.ATOM.getRootElement())) {
                feedType = FeedType.ATOM;
            } else {
                // FIXME Lanzar otra excepcion mejor
                throw new IllegalStateException("Feed desconocido: <" + name + ">");
            }
        } else if (name.equals(feedType.getEntryElement())) {
            entry = new FeedEntry();
            entries = true;
        } else if (name.equals("title")) {
            if (!feedTitle || entries) {
                currentstate = STATE_TITLE;
            }
        } else  if (name.equals("link")) {
            if (feedType == FeedType.ATOM) {
                checkLinkElement(atts);
            } else if (!feedLink || entries) {
                currentstate = STATE_LINK;
            }
        } else if (name.equals(feedType.getUpdateElement()) && entries) {
            currentstate = STATE_PUBDATE;
        } else {
            currentstate = 0;
        }
    }

    public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
        String name = getElementName(localName, qName);
        if (name.equals(feedType.getEntryElement())) {
            entry.setTitle(cleanString(entry.getTitle()));
            entry.setLink(cleanString(entry.getLink()));
            entry.setPubdate(DateFormater.feedDate(cleanString(entry.getPubdate())));
            
            feed.addEntry(entry);
            /*
             * FIXME Es posible que tras todas las entradas haya un elemento title o link 
             * Otra forma sería marcar con alguna variable que si nos llega un element
             * <title> o <link> no pertene a esta entrada
             */
            entry = new FeedEntry();
        } else if (name.equals("title") && !entries) {
            feed.setTitle(feed.getTitle().trim());
            feedTitle = true;
        } else if (name.equals("link") && !entries) {
            if (feed.getWeb() != null) {
                feed.setWeb(feed.getWeb().trim());
                feedLink = true;
            }
        }
        currentstate = 0;
    }

    public void characters(char ch[], int start, int length) {
        String theString = new String(ch, start, length);

        switch (currentstate) {
        case STATE_TITLE:
            if (entries) {
                if (entry.getTitle() == null) {
                    entry.setTitle(theString);
                } else {
                    entry.setTitle(entry.getTitle() + theString);
                }
            } else {
                if (feed.getTitle() == null) {
                    feed.setTitle(theString);
                } else {
                    feed.setTitle(feed.getTitle() + theString);
                }
            }
            break;
        case STATE_LINK:
            if (entries) {
                if (entry.getLink() == null) {
                    entry.setLink(theString);
                } else {
                    entry.setLink(entry.getLink() + theString);
                }
            } else {
                if (feed.getWeb() == null) {
                    feed.setWeb(theString);
                } else {
                    feed.setWeb(feed.getWeb() + theString);
                }
            }
            break;
        case STATE_PUBDATE:
            if (entries) {
                entry.setPubdate(theString);
            }
            break;
        default:
            return;
        }

    }

    private void checkLinkElement(Attributes atts) {
        if ("alternate".equals(atts.getValue("rel"))) {
            String link = atts.getValue("href");
            if (!entries) {
                feed.setWeb(link);
            } else {
                entry.setLink(link);
            }
        }
    }
    
    private String cleanString(String string) {
        if (string != null) {
            String tmp = string.replaceAll("\n", " ");
            tmp = tmp.replaceAll("\t", " ");
            tmp = tmp.replaceAll("\\s\\s+", " ");
            return tmp.trim();
        } else {
            return null;
        }
    }
    
    private String getElementName(String localName, String qName) {
        String name = localName;
        
        if (localName == null || localName.length() == 0) {
            int qual = qName.lastIndexOf(":");
            if (qual > 0) {
                name = qName.substring(qual + 1);
            } else {
                name = qName;
            }
        }
        return name;
    }
}
